#include "master.h"
#include <shlwapi.h>
#include <objbase.h>
#include <dshow.h>
#include <commctrl.h>
#include <shellapi.h>
#include <shlobj.h>
#include <msxml2.h>
#include <stdlib.h>
#include <stdio.h>
#include "mediadet.h"
#include "stream.h"
#include "menu.h"
#include "misc.h"
#include "gui.h"
#include "systray.h"
#include "playlist.h"
#include "comfunct/comfunct.h"

IGraphBuilder *pGraph = NULL;
IMediaControl *pControl = NULL;
IMediaSeeking *pSeek = NULL;
IBasicAudio   *pVolume = NULL;
DWORD dwMaxSliderPos = 0, capable = AM_SEEKING_CanSeekAbsolute | AM_SEEKING_CanGetDuration;
LPTSTR szVolume = NULL, lpSharedMem = NULL;
bool bCanSeek = false;
REFERENCE_TIME FileTimeinNanos = 0;
HANDLE hMemMap = NULL, hInetStreamThread = NULL;
HMODULE hAudioSE = NULL;

bool GetMute(void)
{
     signed long lVolume = 0L;
     pVolume->lpVtbl->get_Volume(pVolume, &lVolume);
     if(lVolume == VOLUME_MUTE)
     {
          return true;
     }
     else return false;
}

bool SetMute(bool bToMute)
{
    extern signed long PlayVolume; 
	if(bToMute == true)
     {
          long lVolume = 0L;
          pVolume->lpVtbl->get_Volume(pVolume, &lVolume);
          PlayVolume = lVolume;
          if(SUCCEEDED(pVolume->lpVtbl->put_Volume(pVolume, VOLUME_MUTE)))
          {
                return true;
          }
          else return false;
     }
     else
     {
          if(SUCCEEDED(pVolume->lpVtbl->put_Volume(pVolume, PlayVolume)))
          {
                 return true;
          }
          else return false;
     }
}

bool DShowInit()
{
     HRESULT hr = 0;
     hr = CoCreateInstance(&CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, &IID_IGraphBuilder, (void**)&pGraph);
     if(FAILED(hr))
     {
         HRErrorInfo(hr);
         ErrorMessage(GetLocalText(55));
         return false;
     }
     pGraph->lpVtbl->QueryInterface(pGraph, &IID_IMediaControl, (void **)&pControl);
     pGraph->lpVtbl->QueryInterface(pGraph, &IID_IBasicAudio, (void **)&pVolume);
     pGraph->lpVtbl->QueryInterface(pGraph, &IID_IMediaSeeking, (void **)&pSeek);
     return true;
}

void DShowRelease()
{
     SAFE_RELEASE(pControl);
     SAFE_RELEASE(pSeek);
     SAFE_RELEASE(pGraph);
     SAFE_RELEASE(pVolume);
     return;
}

void GetOpenFile(HWND hwnd, const bool bTrayed)
{
	OPENFILENAME ofn = {0};
	unsigned long lFilesSelected = 1L;
	static bool bSuccess = false;
	TCHAR szFileName[MAX_PATH] = TEXT("");
	TCHAR szMyMusic[MAX_PATH] = TEXT("");
	LPCTSTR szFilter = GetLocalText(154);
    
	if(pGraph == NULL)
	{
        if(DShowInit() == false)
		{
			return;
		}
	}
	
	if(bSuccess == false)
	{
		GetMyMusicDir(hwnd, szMyMusic);
		bSuccess = true;
    }

	ofn.lStructSize = sizeof(OPENFILENAME);
	ofn.hwndOwner = hwnd;
	ofn.lpstrFilter = szFilter;
	ofn.lpstrFile = szFileName;
	ofn.nFilterIndex = 6;
	ofn.lpstrTitle = GetLocalText(56);
	ofn.nMaxFile = MAX_PATH;
	ofn.Flags = OFN_EXPLORER | OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST | OFN_HIDEREADONLY | OFN_NONETWORKBUTTON | OFN_ALLOWMULTISELECT;
	ofn.lpstrDefExt = TEXT("m3u");
	if(bSuccess == true)
	{
		ofn.lpstrInitialDir = szMyMusic;
	}

	if(GetOpenFileName(&ofn))
	{		
		RAWDATA rd = {0};
		TCHAR* szBufferPointer = _tcschr(szFileName, TEXT('\0'));
		while(*(szBufferPointer + sizeof(TCHAR)) != TEXT('\0'))
		{
			lFilesSelected++;
			szBufferPointer = _tcschr(szBufferPointer + sizeof(TCHAR), TEXT('\0'));
		} /* Find how many files have been selected */

		if(lFilesSelected == 1L) /* if the while loop never gets entered only 1 file has been selected */
		{
            ProcessFile(hwnd, bTrayed, szFileName, NULL);
			return;
		}
		else
		{
            lFilesSelected -= 1L; /* Take away the 1 provided in the initialisation */
			rd.lNumFiles = lFilesSelected;
			rd.szPlaylist = szFileName;
			rd.hwnd = hwnd;
			SendMessage(hwnd, WM_MAKEPLAYLIST, (WPARAM)&rd, 0);
			return;
		}
	}
	else if(CommDlgExtendedError() == 0)
	{
         DShowRelease();
         return;
    }
}

void ProcessFile(HWND hwnd, bool bTrayed, LPCTSTR szFileName, PLAYLISTDATA* pld)
{
	WCHAR* szWideFileName = NULL;
    LPCTSTR szFileExt = PathFindExtension(szFileName);
	HRESULT hr = 0;

	if(pGraph == NULL)
	{
		DShowInit();
	}
        
    if(CompareString(LOCALE_INVARIANT, NORM_IGNORECASE, szFileExt, -1, TEXT(".mid"), -1) != CSTR_EQUAL 
	&& CompareString(LOCALE_INVARIANT, NORM_IGNORECASE, szFileExt, -1, TEXT(".mod"), -1) != CSTR_EQUAL)
    {
		if(PathIsURL(szFileName) == TRUE)
		{
			SetDlgItemText(hwnd, IDI_FILENAME, szFileName);
			SetStatus(hwnd, IDI_STATUS, 0, GetLocalText(7));
            SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDI_CONT_PLAY, 0), (LPARAM)NULL);
            return;
        }
        if((_tcscmp(szFileName, TEXT("")) == 0) || IsFileAudio(szFileName) != true)
        {
			if(pld == NULL)
			{
				ErrorMessage(GetLocalText(57));
				return;
			}
			else
			{
				PostMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDI_PV_REMOVE_TRACK, pld->lCurrentPlayingFile), (LPARAM)0xDEAD);
				if(pld->bShuffle == true)
				{
					pld->lCurrentPlayingFile = GetRandomTrack(pld);
					return ProcessFile(hwnd, bTrayed, pld->PlaylistItems[pld->lCurrentPlayingFile], pld);
				}
				else
				{
					if(pld->lCurrentPlayingFile == (pld->lNumOfFilesinPlaylist - 1))
					{
						pld->lCurrentPlayingFile = 0;
					}
					else pld->lCurrentPlayingFile += 1;
					return ProcessFile(hwnd, bTrayed, pld->PlaylistItems[pld->lCurrentPlayingFile], pld);
				}
			}
        }
    } /*end of midi 'if' */

#ifndef UNICODE /* more DirectShow conversion */

    szWideFileName = UnicodeConversion(szFileName);
    hr = pGraph->lpVtbl->RenderFile(pGraph, szWideFileName, NULL);
	FreePointer(szWideFileName);
	
#else

	hr = pGraph->lpVtbl->RenderFile(pGraph, szFileName, NULL);

#endif

	if(FAILED(hr))
	{
		HRErrorInfo(hr);
    	ErrorMessage(GetLocalText(58));
    	return;
    }
    bCanSeek = (S_OK == pSeek->lpVtbl->CheckCapabilities(pSeek, &capable));
    FileOpen(hwnd, szFileName, bCanSeek);
	if(pld != NULL)
	{
		HWND hPlaylistItems = GetDlgItem(hwnd, IDI_PLAYLIST_VIEW);
		ListView_SetItemState(hPlaylistItems, pld->lCurrentPlayingFile, LVIS_FOCUSED, 0);
	}
	Sleep(50); /* The next line seems to be ineffectual without this */
	SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDI_CONT_PLAY, 0), (LPARAM)NULL);
	if(bTrayed == true)
	{
        UpdateTrayToolTip(hwnd);
    }
    return;
}

void FileOpen(HWND hwnd, LPCTSTR szFileName, bool bSeekable)
{
    extern signed long PlayVolume;
	HMENU hParentMenu = GetMenu(hwnd), hOpenMenu = NULL;
	LPCTSTR szFileTitle = PathFindFileName(szFileName);
	
	SetDlgItemText(hwnd, IDI_FILENAME, szFileName);
	ToggleButton(hwnd, true, false);
	SetStatus(hwnd, IDI_STATUS, 0, GetLocalText(59));
	SetStatus(hwnd, IDI_STATUS, 1, szFileTitle);
	
	if(bSeekable == true)
	{
	     HRESULT hr = 0;
		 pSeek->lpVtbl->SetTimeFormat(pSeek, &TIME_FORMAT_MEDIA_TIME);
	     hr = pSeek->lpVtbl->GetDuration(pSeek, &FileTimeinNanos);
		 if(FAILED(hr))
		 {
			 HRErrorInfo(hr);
			 ErrorMessage(GetLocalText(60));
		 }
	     SendDlgItemMessage(hwnd, IDI_SLIDER, TBM_SETRANGE, true, MAKELONG(0, 100));
	     dwMaxSliderPos = (DWORD)SendDlgItemMessage(hwnd, IDI_SLIDER, TBM_GETRANGEMAX, 0, 0);
	     ShowWindow(GetDlgItem(hwnd, IDI_DIVIDE), SW_SHOW);
	     TotalTimeUpdate(hwnd);
	     ElapsedTimeUpdate(hwnd, 0);
    }
    else
    {
         EnableWindow(GetDlgItem(hwnd, IDI_SLIDER), false);
    }
    
    pVolume->lpVtbl->put_Volume(pVolume, PlayVolume);
	ChangeMenuState(hwnd, MF_ENABLED);
	hOpenMenu = GetSubMenu(hParentMenu, FILE_MENU);
	EnableMenuItem(hOpenMenu, IDI_FILE_OPEN, MF_BYCOMMAND | MF_GRAYED);
	return;
}

void OpenMP3URL(HWND hwnd, LPCTSTR szUrl, bool IsMP3)
{
    extern signed long PlayVolume; 
	HMENU hParentMenu = GetMenu(hwnd), hControlMenu = NULL, hOpenMenu = NULL;
	if(IsMP3 == true)
	{
		ASEFUNC InetThread = NULL;
		DWORD dwNotNeeded = 0;
		size_t s_tUrlLen = (_tcslen(szUrl) * sizeof(TCHAR));
		if(s_tUrlLen < (MAX_PATH * sizeof(TCHAR)))
		{
			s_tUrlLen = MAX_PATH * sizeof(TCHAR);
		}
		  
		EnableWindow(GetDlgItem(hwnd, IDI_CONT_PLAY), false);
		  
		hAudioSE = LoadLibrary(TEXT("AudioSE.dll"));
		InetThread = (ASEFUNC)GetProcAddress(hAudioSE, "ASEThread");

		hMemMap = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, (DWORD)s_tUrlLen, TEXT("URLMap"));
		if(hMemMap == NULL || GetLastError() == ERROR_ALREADY_EXISTS)
		{
			ErrorInfo();
			ErrorMessage(GetLocalText(61));
			return;
		}
		lpSharedMem = (LPTSTR)MapViewOfFile(hMemMap, FILE_MAP_ALL_ACCESS, 0, 0, s_tUrlLen);
		if(lpSharedMem == NULL)
		{
			ErrorInfo();
			ErrorMessage(GetLocalText(62));
			return;
		}
		CopyMemory((LPVOID)lpSharedMem, szUrl, s_tUrlLen);
		ShowWindow(GetDlgItem(hwnd, IDI_DIVIDE), SW_SHOW);
		hInetStreamThread = CreateThread(NULL, 0, InetThread, (LPVOID)s_tUrlLen, 0, &dwNotNeeded);
	}
	if(pVolume == NULL)
	{
		DShowInit();
	}

	bCanSeek = false;
	pVolume->lpVtbl->put_Volume(pVolume, PlayVolume);
	SendDlgItemMessage(hwnd, IDI_VOLUME, TBM_SETPOS, true, 0);
	szVolume = VolumeTextFromLong(0L, szVolume);
	SetStatus(hwnd, IDI_STATUS, 2, szVolume);

	hControlMenu = GetSubMenu(hParentMenu, CONTROL_MENU);
	if(IsMP3 == true)
	{
		ToggleButton(hwnd, true, true);
		EnableMenuItem(hControlMenu, IDI_CONT_PLAY, MF_BYCOMMAND | MF_GRAYED);
		EnableMenuItem(hControlMenu, IDI_CONT_PAUSE, MF_BYCOMMAND | MF_GRAYED);
		EnableWindow(GetDlgItem(hwnd, IDI_CONT_PAUSE), false);
	}
	else
	{
		ToggleButton(hwnd, true, false);
	}

	EnableWindow(GetDlgItem(hwnd, IDI_SLIDER), false);
	ChangeMenuState(hwnd, MF_ENABLED);
	hOpenMenu = GetSubMenu(hParentMenu, FILE_MENU);
	EnableMenuItem(hOpenMenu, IDI_FILE_OPEN, MF_BYCOMMAND | MF_GRAYED);
	DrawMenuBar(hwnd);
	return;
}

void CommandLineOpen(LPTSTR szFileName, HWND hwnd)
{
	AP_DATACOPY adc = {0};
	
	PathUnquoteSpaces(szFileName);
	adc.szFileName = szFileName;
	adc.hwnd = hwnd;
    if(CheckForOtherInstances(&adc) == true) return;


	if(GetPlaylistType(szFileName) != PLAYLIST_ERROR)
    {
        TCHAR szFileNameCopy[MAX_PATH] = TEXT("");
		memcpy(szFileNameCopy, szFileName, ((_tcslen(szFileName) + 1) * sizeof(TCHAR)));
		PathRemoveFileSpec(szFileNameCopy); 
		SetCurrentDirectory(szFileNameCopy); /* This helps to resolve relative file paths in playlists by using the playlist's path as the root */
		SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDI_PLAYLIST_COMMANDLINE, 0), (LPARAM)szFileName);
        return;
    }
	
    ProcessFile(hwnd, IsIconic(hwnd), szFileName, NULL);
	return;
}

void CloseStream(HWND hwnd, HANDLE* hDownloadedStream)
{
    HMENU hParentMenu = GetMenu(hwnd), hOpenMenu = NULL; 
	
	DShowRelease();
    SetStatus(hwnd, IDI_STATUS, 0, GetLocalText(7));
    SetStatus(hwnd, IDI_STATUS, 1, TEXT(""));
    SetDlgItemText(hwnd, IDI_CONT_PAUSE, GetLocalText(2));
	ClearTimes(hwnd);
    ToggleButton(hwnd, false, false);
    ChangeMenuState(hwnd, MF_GRAYED);
	hOpenMenu = GetSubMenu(hParentMenu, FILE_MENU);
    EnableMenuItem(hOpenMenu, IDI_FILE_OPEN, MF_BYCOMMAND | MF_ENABLED);
    EnableMenuItem(hOpenMenu, IDI_FILE_SAVESTREAM, MF_BYCOMMAND | MF_GRAYED);
    DrawMenuBar(hwnd);
	if(hDownloadedStream != NULL)
	{
	   CloseHandle(*hDownloadedStream);
	   *hDownloadedStream = NULL;
	}
    return;
}

void StopInternetDownload()
{
	STOPPROC StopDownloading = NULL;
	StopDownloading = (STOPPROC)GetProcAddress(hAudioSE, "StopDownloading");
	StopDownloading();
	return;
}

void SaveAudioStream(HANDLE hTempStream, LPTSTR szNewPath)
{
	static HANDLE hTempAudioFile = NULL;
	
	if(hTempStream != NULL)
	{
		hTempAudioFile = hTempStream;
	}
	else
	{
		HANDLE hNewFile = NULL;
		DWORD dwFileSize = 0, dwBytesRead = 0, dwBytesWritten = 0;
		BYTE* byFileData = NULL;
		
		hNewFile = CreateFile(szNewPath, GENERIC_WRITE | DELETE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
		if(hNewFile == NULL || hNewFile == INVALID_HANDLE_VALUE)
		{
			ErrorInfo();
			ErrorMessage(GetLocalText(63));
			return;
		}
		dwFileSize = GetFileSize(hTempAudioFile, NULL);
		
		byFileData = malloc(dwFileSize);
		if(byFileData != NULL)
		{
			if(ReadFile(hTempAudioFile, &byFileData[0], dwFileSize, &dwBytesRead, NULL) != false)
			{
				WriteFile(hNewFile, &byFileData[0], dwBytesRead, &dwBytesWritten, NULL);
			}
			else 
			{
				ErrorInfo();
				ErrorMessage(GetLocalText(64));
				DeleteFile(hNewFile);
			}
		}
		CloseHandle(hNewFile);
		CloseHandle(hTempAudioFile);
		free(byFileData);
	}
	return;
}
